package com.jiashihui.schema_lgorithm.java.reflect;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author jiashihui
 * @date 2019/04/04
 * <p>
 * 反射机制，它允许我们在运行时发现和使用类的信息。
 * <p>
 * 使用功能”.class”来创建Class对象的引用时，不会自动初始化该Class对象，使用forName()会自动初始化该Class对象。
 * Class.forName(className)方法，内部实际调用的方法是  Class.forName(className,true,classloader);第2个boolean参数表示类是否需要初始化，  Class.forName(className)默认是需要初始化。一旦初始化，就会触发目标对象的 static块代码执行，static参数也也会被再次初始化。
 * ClassLoader.loadClass(className)方法，内部实际调用的方法是  ClassLoader.loadClass(className,false);第2个 boolean参数，表示目标对象是否进行链接，false表示不进行链接，由上面介绍可以，不进行链接意味着不进行包括初始化等一些列步骤，那么静态块和静态对象就不会得到执行
 * <p>
 * 为了使用类而做的准备工作一般有以下3个步骤：
 * （1）加载：由类加载器完成，找到对应的字节码，创建一个Class对象
 * （2）链接：验证类中的字节码，为静态域分配空间
 * （3）初始化：如果该类有超类，则对其初始化，执行静态初始化器和静态初始化块
 * <p>
 * java类装载过程：加载--验证-准备-解析--初始化--使用--卸载
 */
public class TestMain {
    public static void main(String[] args) throws Exception {
        // 不会初始化静态块
        Class clazz1 = Base.class;
        System.out.println("------------------------------------------");
        // 会初始化
        Class clazz2 = Class.forName("com.jiashihui.schema_lgorithm.java.reflect.Base");

        System.out.println("------------------------------------------");

        /**
         * Class类与java.lang.reflect类库一起对反射进行了支持，该类库包含Field、Method和Constructor类，这些类的对象由JVM在启动时创建，用以表示未知类里对应的成员。
         * 这样的话就可以使用Contructor创建新的对象，用get()和set()方法获取和修改类中与Field对象关联的字段，用invoke()方法调用与Method对象关联的方法。
         * 另外，还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法，以返回表示字段、方法、以及构造器对象的数组，这样，对象信息可以在运行时被完全确定下来，而在编译时不需要知道关于类的任何事情。
         */
        Person person = new Person("luoxn28", 23);
        Class clazz = person.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String key = field.getName();
            if("serialVersionUID".equals(key)){
                continue;
            }
            // 反射机制的PropertyDescriptor去根据这个属性名字的字符串去得到属性的值
            PropertyDescriptor descriptor = new PropertyDescriptor(key, clazz);
            // getReadMethod()方法调用类的get函数，可以通过getWriteMethod()方法来调用类的set方法。
            Method method = descriptor.getReadMethod();
            Object value = method.invoke(person);
            System.out.println(key + ":" + value);
        }
    }
}

/**
 * getFields()：获得某个类的所有的公共（public）的字段，包括父类中的字段。
 * getDeclaredFields()：获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段。
 * 同样类似的还有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods()，这两者分别表示获取某个类的方法、构造函数。
 */
